package com.commerce.product.utils;

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Component;

import java.util.Collections;
import java.util.concurrent.TimeUnit;

/**
 * Redis工具类，提供分布式锁、限流等功能
 */
@Slf4j
@Component
public class RedisUtil {

    @Autowired
    private RedisTemplate<String, Object> redisTemplate;

    // 分布式锁的Lua脚本
    private static final String LOCK_SCRIPT = 
        "if redis.call('get', KEYS[1]) == ARGV[1] then " +
        "return redis.call('del', KEYS[1]) " +
        "else " +
        "return 0 " +
        "end";

    // 限流的Lua脚本（滑动窗口）
    private static final String RATE_LIMIT_SCRIPT = 
        "local key = KEYS[1] " +
        "local window = tonumber(ARGV[1]) " +
        "local limit = tonumber(ARGV[2]) " +
        "local current = tonumber(ARGV[3]) " +
        "redis.call('zremrangebyscore', key, '-inf', current - window) " +
        "local count = redis.call('zcard', key) " +
        "if count < limit then " +
        "redis.call('zadd', key, current, current) " +
        "redis.call('expire', key, window) " +
        "return 1 " +
        "else " +
        "return 0 " +
        "end";

    // 库存预减的Lua脚本
    private static final String STOCK_DEDUCT_SCRIPT = 
        "local key = KEYS[1] " +
        "local quantity = tonumber(ARGV[1]) " +
        "local stock = redis.call('get', key) " +
        "if stock == false then " +
        "return -1 " +
        "end " +
        "stock = tonumber(stock) " +
        "if stock >= quantity then " +
        "redis.call('decrby', key, quantity) " +
        "return stock - quantity " +
        "else " +
        "return -2 " +
        "end";

    /**
     * 获取分布式锁
     * @param key 锁的key
     * @param value 锁的value（通常是UUID）
     * @param expireTime 过期时间（秒）
     * @return 是否获取成功
     */
    public boolean tryLock(String key, String value, long expireTime) {
        try {
            Boolean result = redisTemplate.opsForValue().setIfAbsent(key, value, expireTime, TimeUnit.SECONDS);
            return Boolean.TRUE.equals(result);
        } catch (Exception e) {
            log.error("获取分布式锁失败: key={}, error={}", key, e.getMessage());
            return false;
        }
    }

    /**
     * 释放分布式锁
     * @param key 锁的key
     * @param value 锁的value
     * @return 是否释放成功
     */
    public boolean releaseLock(String key, String value) {
        try {
            DefaultRedisScript<Long> script = new DefaultRedisScript<>();
            script.setScriptText(LOCK_SCRIPT);
            script.setResultType(Long.class);
            Long result = redisTemplate.execute(script, Collections.singletonList(key), value);
            return Long.valueOf(1).equals(result);
        } catch (Exception e) {
            log.error("释放分布式锁失败: key={}, error={}", key, e.getMessage());
            return false;
        }
    }

    /**
     * 限流检查（滑动窗口算法）
     * @param key 限流key
     * @param windowSize 时间窗口大小（秒）
     * @param limit 限制次数
     * @return 是否通过限流检查
     */
    public boolean checkRateLimit(String key, int windowSize, int limit) {
        try {
            long currentTime = System.currentTimeMillis() / 1000;
            DefaultRedisScript<Long> script = new DefaultRedisScript<>();
            script.setScriptText(RATE_LIMIT_SCRIPT);
            script.setResultType(Long.class);
            Long result = redisTemplate.execute(script, Collections.singletonList(key), 
                                              windowSize, limit, currentTime);
            return Long.valueOf(1).equals(result);
        } catch (Exception e) {
            log.error("限流检查失败: key={}, error={}", key, e.getMessage());
            return false;
        }
    }

    /**
     * 库存预减
     * @param key 库存key
     * @param quantity 扣减数量
     * @return 扣减后的库存数量，-1表示库存不存在，-2表示库存不足
     */
    public long deductStock(String key, int quantity) {
        try {
            DefaultRedisScript<Long> script = new DefaultRedisScript<>();
            script.setScriptText(STOCK_DEDUCT_SCRIPT);
            script.setResultType(Long.class);
            Long result = redisTemplate.execute(script, Collections.singletonList(key), quantity);
            return result != null ? result : -1;
        } catch (Exception e) {
            log.error("库存预减失败: key={}, quantity={}, error={}", key, quantity, e.getMessage());
            return -1;
        }
    }

    /**
     * 设置库存
     * @param key 库存key
     * @param stock 库存数量
     * @param expireTime 过期时间（秒）
     */
    public void setStock(String key, int stock, long expireTime) {
        try {
            redisTemplate.opsForValue().set(key, stock, expireTime, TimeUnit.SECONDS);
        } catch (Exception e) {
            log.error("设置库存失败: key={}, stock={}, error={}", key, stock, e.getMessage());
        }
    }

    /**
     * 获取库存
     * @param key 库存key
     * @return 库存数量
     */
    public Integer getStock(String key) {
        try {
            Object value = redisTemplate.opsForValue().get(key);
            return value != null ? Integer.valueOf(value.toString()) : null;
        } catch (Exception e) {
            log.error("获取库存失败: key={}, error={}", key, e.getMessage());
            return null;
        }
    }

    /**
     * 增加计数器
     * @param key 计数器key
     * @param delta 增加的值
     * @param expireTime 过期时间（秒）
     * @return 增加后的值
     */
    public Long increment(String key, long delta, long expireTime) {
        try {
            Long result = redisTemplate.opsForValue().increment(key, delta);
            if (expireTime > 0) {
                redisTemplate.expire(key, expireTime, TimeUnit.SECONDS);
            }
            return result;
        } catch (Exception e) {
            log.error("计数器增加失败: key={}, delta={}, error={}", key, delta, e.getMessage());
            return null;
        }
    }

    /**
     * 设置值
     * @param key 键
     * @param value 值
     * @param expireTime 过期时间（秒）
     */
    public void set(String key, Object value, long expireTime) {
        try {
            if (expireTime > 0) {
                redisTemplate.opsForValue().set(key, value, expireTime, TimeUnit.SECONDS);
            } else {
                redisTemplate.opsForValue().set(key, value);
            }
        } catch (Exception e) {
            log.error("设置值失败: key={}, error={}", key, e.getMessage());
        }
    }

    /**
     * 获取值
     * @param key 键
     * @return 值
     */
    public Object get(String key) {
        try {
            return redisTemplate.opsForValue().get(key);
        } catch (Exception e) {
            log.error("获取值失败: key={}, error={}", key, e.getMessage());
            return null;
        }
    }

    /**
     * 删除key
     * @param key 键
     * @return 是否删除成功
     */
    public boolean delete(String key) {
        try {
            return Boolean.TRUE.equals(redisTemplate.delete(key));
        } catch (Exception e) {
            log.error("删除key失败: key={}, error={}", key, e.getMessage());
            return false;
        }
    }

    /**
     * 检查key是否存在
     * @param key 键
     * @return 是否存在
     */
    public boolean exists(String key) {
        try {
            return Boolean.TRUE.equals(redisTemplate.hasKey(key));
        } catch (Exception e) {
            log.error("检查key存在失败: key={}, error={}", key, e.getMessage());
            return false;
        }
    }
}
